package com.lemon.cloud.oauth.config;

import com.lemon.cloud.comm.constants.OAuth2Constant;
import com.lemon.cloud.oauth.support.CustomeOAuth2AccessTokenGenerator;
import com.lemon.cloud.oauth.support.core.CustomeOAuth2TokenCustomizer;
import com.lemon.cloud.oauth.support.core.FormIdentityLoginConfig;
import com.lemon.cloud.oauth.support.core.UserDetailsAuthenticationProvider;
import com.lemon.cloud.oauth.support.handler.AuthenticationFailureEventHandler;
import com.lemon.cloud.oauth.support.handler.AuthenticationSuccessEventHandler;
import com.lemon.cloud.oauth.support.mp.OAuth2ResourceOwnerMpAuthenticationConverter;
import com.lemon.cloud.oauth.support.mp.OAuth2ResourceOwnerMpAuthenticationProvider;
import com.lemon.cloud.oauth.support.password.OAuth2ResourceOwnerPasswordAuthenticationConverter;
import com.lemon.cloud.oauth.support.password.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import com.lemon.cloud.oauth.support.sms.OAuth2ResourceOwnerSmsAuthenticationConverter;
import com.lemon.cloud.oauth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationConverter;

import java.util.Arrays;

/**
 * `@EnableAuthorizationServer`：这个注解标注这是一个认证中心
 * 继承AuthorizationServerConfigurerAdapter
 *
 * @author HuangDS
 */
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfiguration {

    private final OAuth2AuthorizationService authorizationService;

    @Value("${spring.application.name}")
    private String applicationName;

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        // OAuth 2.1 默认配置
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        // 使用 HttpSecurity 获取 OAuth 2.1 配置中的 OAuth2AuthorizationServerConfigurer 对象
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = http
                .getConfigurer(OAuth2AuthorizationServerConfigurer.class);
        // 个性化认证授权端点
        authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {
            // 注入自定义的授权认证Converter
            tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter())
                    // 登录成功处理器
                    .accessTokenResponseHandler(new AuthenticationSuccessEventHandler())
                    // 登录失败处理器
                    .errorResponseHandler(new AuthenticationFailureEventHandler());
            // 个性化客户端认证
        }).clientAuthentication(oAuth2ClientAuthenticationConfigurer ->
                // 处理客户端认证异常
                oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new AuthenticationFailureEventHandler()))
                // 授权码端点个性化confirm页面
                .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint
                        .consentPage(OAuth2Constant.CUSTOM_CONSENT_PAGE_URI));

        //
        AuthorizationServerSettings.Builder builder = AuthorizationServerSettings.builder();
        builder.issuer(OAuth2Constant.PROJECT_LICENSE);
        //这里可以自定义请求URI
        DefaultSecurityFilterChain securityFilterChain = authorizationServerConfigurer
                // redis存储token的实现
                .authorizationService(authorizationService)
                .authorizationServerSettings(builder.build())
                // 授权码登录的登录页个性化
                .and()
                .apply(new FormIdentityLoginConfig(applicationName))
                .and()
                .build();

        //自定义接口
        // 注入自定义授权模式实现
        addCustomOAuth2GrantAuthenticationProvider(http);
        return securityFilterChain;
    }

    /**
     * 令牌生成规则实现 </br>
     * client:username:uuid
     *
     * @return OAuth2TokenGenerator
     */
    @Bean
    public OAuth2TokenGenerator oAuth2TokenGenerator() {
        CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
        // 注入Token 增加关联用户信息
        accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
        return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
    }

    /**
     * request -> xToken 注入请求转换器
     *
     * @return DelegatingAuthenticationConverter
     */
    private AuthenticationConverter accessTokenRequestConverter() {
        return new DelegatingAuthenticationConverter(
                Arrays.asList(new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
                        new OAuth2ResourceOwnerSmsAuthenticationConverter(),
                        new OAuth2ResourceOwnerMpAuthenticationConverter(),
                        new OAuth2AuthorizationCodeRequestAuthenticationConverter()));
    }

    /**
     * 注入授权模式实现提供方
     * <p>
     * 1. 密码模式 </br>
     * 2. 短信登录 </br>
     */
    @SuppressWarnings("unchecked")
    private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);

        OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);

        OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
                authenticationManager, authorizationService, oAuth2TokenGenerator());

        OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
                authenticationManager, authorizationService, oAuth2TokenGenerator());

        OAuth2ResourceOwnerMpAuthenticationProvider resourceOwnerMpAuthenticationProvider = new
                OAuth2ResourceOwnerMpAuthenticationProvider(authenticationManager, authorizationService, oAuth2TokenGenerator());

        // 处理 UsernamePasswordAuthenticationToken
        http.authenticationProvider(new UserDetailsAuthenticationProvider());
        // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
        http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
        // 处理 OAuth2ResourceOwnerSmsAuthenticationToken
        http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
        // 处理 OAuth2ResourceOwnerMpAuthenticationToken
        http.authenticationProvider(resourceOwnerMpAuthenticationProvider);
    }

}
